#include "oj_compile_run/oj_runer.h"
namespace ojlb {
namespace oj_compiler_run {


std::pair<bool, int> OjRuner::run(const oj_common::OjCodeFile& oj_code_file, const time_t& max_cpu_sec, const size_t& max_memory_kb) {
    // 检查运行环境
    checkRunEnv(oj_code_file);

    // 清理运行环境
    cleanRunEnv(oj_code_file);
    
    // 创建子进程虚拟运行器
    pid_t fork_result = fork();

    // 使得标准流重定向
    oj_common::OjStreaEdirection ojse_err = oj_common::OjStreaEdirection(oj_code_file.getErrorPath().c_str()); // 无需释放, 反正子进程会在执行完后被回收
    ojse_err.setNewStdErrFilePid(); // 重定向标准错误
    oj_common::OjStreaEdirection ojse_out = oj_common::OjStreaEdirection(oj_code_file.getResultPath().c_str()); // 无需释放, 反正子进程会在执行完后被回收
    ojse_out.setNewStdOutFilePid(); // 重定向标准输出


    // 运行子进程虚拟运行器
    if (fork_result < 0) { // 异常
        handleForkError();
    } else if (fork_result == 0) { // 子进程
        handleChildProcess(oj_code_file, max_cpu_sec, max_memory_kb); // TODO: 这里哪怕子进程异常了估计也无法察觉...以后改...
    } else { // 父进程
        return handleParentProcess(fork_result, ojse_err, ojse_out);
    }

    return std::make_pair(false, -1);
}

void OjRuner::checkRunEnv(const oj_common::OjCodeFile& oj_code_file) {
    if (!oj_code_file.isExistOfSourcePath()) {
        oj_common::OjLog::warn("用户的目标代码文件不存在, 无法进行运行");
        exit(-1); // TODO: 抛出异常
    }
}

void OjRuner::cleanRunEnv(const oj_common::OjCodeFile& oj_code_file) {
    if (oj_code_file.isExistOfResultPath()) {
        if (remove(oj_code_file.getResultPath().c_str()) == 0) {
            oj_common::OjLog::debug("删除了之前运行遗留下来的结果文件");
        } else {
            oj_common::OjLog::warn("清理运行环境失败, 旧的结果文件无法删除");
            exit(-1); // TODO: 抛出异常
        }
    }
}

void OjRuner::handleForkError() {
    oj_common::OjLog::error("虚拟运行器的子进程创建异常, 检查创建虚拟运行器的 fork() 是否出现问题");
    exit(-1); // TODO: 抛出异常
}

void OjRuner::handleChildProcess(const oj_common::OjCodeFile& oj_code_file, const time_t& max_cpu_sec, const size_t& max_memory_kb) {
    /** 运行结果情况分析
     * 1. 代码运行完, 结果正确
     * 2. 代码运行完, 结果不正确
     * 3. 代码没运行完, 中途出现异常
     * 运行模块只管运行不检查结果是否正确, 但需要把异常结果导出到错误文件中
     * 因此运行模块在运行完后生成目标文件, 在没运行完后生成错误文件(结果正不正确交给后续的用例测试)
     */
    setCpuTimeAndMemoryLimit(max_cpu_sec, max_memory_kb); // 设置算法限制

    // 虚拟运行器运行目标
    std::string programming_language = oj_code_file.getProgrammingLanguage();


    if (programming_language == "c" || programming_language == "cpp") {
        execl(oj_code_file.getObjectPath().c_str(), oj_code_file.getObjectPath().c_str(), nullptr);
    } else if (programming_language == "java") {
        std::string class_path = oj_code_file.getCodeFileName().c_str();
        size_t pos = class_path.find_last_of('/');

        std::string class_dir = (pos == std::string::npos) ? "." : class_path.substr(0, pos + 1);
        std::string class_name = (pos == std::string::npos) ? class_path : class_path.substr(pos + 1);

        execlp("java", "java", "-cp", class_dir.c_str(), class_name.c_str(), nullptr);
    } else if (programming_language == "python") {
        ; // execlp("python", "python", oj_code_file.getSourcePath().c_str(), nullptr); // TODO: 先不做 Python 的
        exit(-1); // TODO: 抛出异常, 暂时不支持
    } else {
        exit(-1); // TODO: 抛出异常, 用户提供了不支持的编译语言, 等待未来项目更新
    } // TODO: 这里还没有把编译版本也算上...
    
    exit(-1); // TODO: 抛出异常, 虚拟运行器的子进程程序替换出现问题
}

std::pair<bool, int> OjRuner::handleParentProcess(const pid_t& fork_result, oj_common::OjStreaEdirection ojse_err, oj_common::OjStreaEdirection ojse_out) {
    int status = 0;
    waitpid(fork_result, &status, 0); // TODO: 暂时不对子进程的返回结果做关心, 阻塞等待就行
    
    ojse_err.resetNewStdErrFilePid(); // 恢复标准流重定向
    ojse_out.resetNewStdOutFilePid(); // 恢复标准流重定向

    if(WIFSIGNALED(status)) { // 子进程因信号终止返回非零值
        oj_common::OjLog::info("运行失败, 用户提交的代码中存在运行错误");
        return std::make_pair(false, WTERMSIG(status));
    } else {
        return std::make_pair(true, WTERMSIG(status));
    }
    // TODO: 这里对于 Python 的处理还有漏洞, 和 python 的特性有关
    // TODO: 这里有可能需要使用沙盒机制来解决一些异常情况, 例如用户没有检测动态内存返回 null 导致的情况...
}

void OjRuner::setCpuTimeAndMemoryLimit(const int& max_cpu_sec, const int& max_memory_kb) {
    struct rlimit rl_cpu;
    rl_cpu.rlim_max = RLIM_INFINITY;
    rl_cpu.rlim_cur = max_cpu_sec;
    setrlimit(RLIMIT_CPU, &rl_cpu);
    
    struct rlimit rl_mem;
    rl_mem.rlim_max = RLIM_INFINITY;
    rl_mem.rlim_cur = max_memory_kb * size_of_kb;
    setrlimit(RLIMIT_AS, &rl_mem);
}


} // namespace oj_compiler_run end
} // namespace ojlb end
